package dfl.bysj.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.generator.config.IFileCreate;
import dfl.bysj.config.RedisKey;
import dfl.bysj.config.WXAuth;
import dfl.bysj.config.WxService;
import dfl.bysj.config.model.VO.UserDto;
import dfl.bysj.config.model.VO.WxSession;
import dfl.bysj.config.model.WxUserInfo;
import dfl.bysj.entity.*;
import dfl.bysj.entity.VO.ReserveSUCCVO;
import dfl.bysj.entity.VO.StudentVO;
import dfl.bysj.exception.Assert;
import dfl.bysj.exception.UnifiedException;
import dfl.bysj.mapper.*;
import dfl.bysj.result.ResponseEnum;
import dfl.bysj.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import dfl.bysj.util.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author DFL
 * @since 2022-04-26
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private StringRedisTemplate redisTemplate;
    private RedisTemplate redisTemplate_obj;

    private WxService wxService;

    private UserMapper userMapper;

    private UserTeacherStudentMapper userTeacherStudentMapper;

    private TeacherMapper teacherMapper;

    private StudentMapper studentMapper;

    private AcademyService academyService;


    private GradeService gradeService;

    private List<User> users;

    private AtomicReference<User> userAtomicReference= new AtomicReference<>(new User());

    private UpdateBindService updateBindService;

    private StudentSeatRelationService studentSeatRelationService;

    private HonestGradeService honestGradeService;

    @Autowired
    public void setStudentSeatRelationService(StudentSeatRelationService studentSeatRelationService) {
        this.studentSeatRelationService = studentSeatRelationService;
    }

    @Autowired
    public void setHonestGradeService(HonestGradeService honestGradeService) {
        this.honestGradeService = honestGradeService;
    }

    @Autowired
    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }

    @Autowired
    public void setUpdateBindService(UpdateBindService updateBindService) {
        this.updateBindService = updateBindService;
    }

    @Autowired
    private StudentService studentService;

//    @Autowired
//    public void setStudentService(StudentService studentService) {
//        this.studentService = studentService;
//    }

    @Autowired
    public void setGradeService(GradeService gradeService) {
        this.gradeService = gradeService;
    }

    @Autowired
    public void setAcademyService(AcademyService academyService) {
        this.academyService = academyService;
    }

    @Autowired
    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Autowired
    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate_obj = redisTemplate;
    }



    @Autowired
    public void setStudentMapper(StudentMapper studentMapper) {
        this.studentMapper = studentMapper;
    }

    @Autowired
    public void setTeacherMapper(TeacherMapper teacherMapper) {
        this.teacherMapper = teacherMapper;
    }

    @Autowired
    public void setUserTeacherStudentMapper(UserTeacherStudentMapper userTeacherStudentMapper) {
        this.userTeacherStudentMapper = userTeacherStudentMapper;
    }

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Autowired
    public void setWxService(WxService wxService) {
        this.wxService = wxService;
    }



    @Value("${wx.appid}")
    private String appid;

    @Value("${wx.secret}")
    private String secret;



    @Override
    public String getSessionId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
        url = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
        String res = HttpUtil.get(url);
        String s = UUID.randomUUID().toString();

        if (redisTemplate.opsForValue().get(RedisKey.WX_SESSION_ID + s)==null){
            redisTemplate.opsForValue().set(RedisKey.WX_SESSION_ID + s, res);

        }
        return s;
    }



    /**
     * 1、通过wxauth中的值，要对它进行解密
     * 2、解密完成之后，会获取到微信用户信息，其中包含openId，性别，昵称，头像等信息
     * 3、openId是唯一的，需要去user表中查询openId是否存在，存在，已此用户的身份登录成功
     * 4、不存在，新用户，注册流程，登陆成功
     * 5、使用jwt，生成一个token，提供给前端token令牌，用户在下次访问的时候，携带token来访问
     * 6、后端通过对token的验证，知道此用户是否处于登录状态，以及是那个用户登录的
     * @param
     * @return
     */
//    @Transactional
    @Override
    public ConcurrentHashMap<String,Object> authLogin(WXAuth wxAuth) throws Exception {
        log.info("wxAuth:{}",wxAuth.toString());
        String json=wxService.wxDecrypt(wxAuth.getEncryptedData(), wxAuth.getSessionId(), wxAuth.getIv());
        WxUserInfo wxUserInfo= JSON.parseObject(json, WxUserInfo.class);
        String openId=getOpenId(wxAuth.getSessionId());
        log.info(wxUserInfo.toString());
        User user=getUserByOpenId(openId);

//        Map map= (Map) redisTemplate_obj.opsForValue().get(RedisKey.UPDATE_INFORMATION_STU_APPLY+user.getOpenId());
//        Assert.isNull(map,ResponseEnum.UPDATE_BIND_APPLIED);
//        Assert.isTrue(!getStudent_bind(user.getOpenId()),ResponseEnum.UPDATE_BIND_APPLIED);
        //中间值
        UserDto userDto=new UserDto();
        userDto.setDto_info("DFL");
        userDto.setUser(user);
        //如果user表中查询不到用户，说明用户未注册
        /**
         * 用户注册：
         *  1、插入用户表
         *  2、返回未注册的信息
         *  3、跳转页面到学生信息中
         *  4、信息填写完成后，后台核验学生信息是否正确，正确就允许绑定
         */
         if (user==null){
             user=new User(wxUserInfo.getNickName(),
                     wxUserInfo.getGender(),
                     wxUserInfo.getAvatarUrl(),
                     openId,
                     wxUserInfo.getUnionId());

             userMapper.insert(user);
             updateRedisOfUsers();


             throw new UnifiedException(ResponseEnum.LOGIN_MOBILE_ERROR);
         }
        //如果user表中查询到用户以后，查看关系表中是否存在绑定
        //用户未绑定，就跳转页面执行绑定
        log.info(user.toString());
        UserTeacherStudent userTeacherStudent=userTeacherStudentMapper.selectOne(new QueryWrapper<UserTeacherStudent>()
                .eq("use_id", user.getId())
                .eq("check_state", true));

        if (userTeacherStudent==null){
            //1、检查是否申请了修改绑定，申请了则返回错误信息
            UpdateBind updateBind=updateBindService.getUpdateBindByUseId(user.getId());
            if (updateBind!=null){
                throw new UnifiedException(ResponseEnum.UPDATE_BIND_APPLIED);
            }else{
                throw new UnifiedException(ResponseEnum.LOGIN_MOBILE_BIND_ERROR);
            }
        }
        //用户已绑定，判断该微信用户是学生还是老师
        //先判断关系表中的审核状态，审核状态为false为不通过，返回错误信息
        Assert.isTrue(userTeacherStudent.getCheckState(),ResponseEnum.USER_BIND_CHECK_STATE);

        //根据角色跳转到不同的页面
        ConcurrentHashMap<String,Object> resultMap=new ConcurrentHashMap<String,Object>();

        //添加token
        String token=JWTUtils.sign(user.getId());
        resultMap.put("token", token);

        if (userTeacherStudent.getStuId()==null){
            //教师登录
            Teacher teacher= teacherMapper.selectById(userTeacherStudent.getTeaId());
            userDto.setTeacher(teacher);
            resultMap.put("teacher", teacher);

        }else{

            Student student=studentService.getStudentById(userTeacherStudent.getStuId());
            //学生登录
//            //2、检查是否预约了座位，预约了就保存返回给前端
//           StudentSeatRelation studentSeatRelation= studentSeatRelationService
//                    .getStudentSeatRealation(student.getStuId(),false);
//
//            if (studentSeatRelation!=null){
//                //1、判断是否已超过预约时间
//                resultMap.put("reserve", ReserveSUCCVO.getReserveSUCCVO(studentSeatRelation));
//            }
//            //3、获得个人的信用分数
//            HonestGrade honestGrade=honestGradeService.getHonestGradeByStuUseId(userTeacherStudent.getStuUseId());
//            if (honestGrade!=null){
//                resultMap.put("honestGrade", honestGrade);
//            }


            userDto.setStudent(student);
            Grade grade=gradeService.getGradeById(student.getGraId());
            Academy academy=academyService.getAcademyById(grade.getAcaId());



            userDto.setGrade(grade);
            userDto.setAcademy(academy);

            resultMap.put("student", student);
            resultMap.put("grade", grade);
            resultMap.put("academy", academy);
        }

        resultMap.put("stuUseId", userTeacherStudent.getStuUseId());
        //token缓存
        redisTemplate_obj.opsForValue().setIfAbsent(RedisKey.TOKEN+token, userDto);

        return resultMap;
    }

    private String getOpenId(String sessionId){
      String res=  redisTemplate.opsForValue().get(RedisKey.WX_SESSION_ID+sessionId);
        WxSession wxSession=JSON.parseObject(res, WxSession.class);
        return wxSession.getOpenid();
    }

    @Override
    public User getUserByOpenId(String openId) {
        getUsers();
        users.stream()
                .filter(s-> Objects.equals(s.getOpenId(), openId))
                .forEach(s-> userAtomicReference.set(s));

        if (userAtomicReference.get().getId()==null){
            return null;
        }
        return userAtomicReference.get();
    }

    @Override
    public User getUserById(Integer id) {
        getUsers();
        users.stream()
                .filter(s-> Objects.equals(s.getId(), id))
                .forEach(s-> userAtomicReference.set(s));
        if (userAtomicReference.get().getId()==null){
            return null;
        }
        return userAtomicReference.get();
    }


    private void getUsers(){
        users= (List<User>) redisTemplate_obj.opsForValue().get(RedisKey.ALL_USER);
        if (users==null){
            updateRedisOfUsers();
        }
    }

    private void updateRedisOfUsers(){
        users=list();
        redisTemplate_obj.opsForValue().set(RedisKey.ALL_USER,users,7, TimeUnit.DAYS);
    }

    /**
     * 学生注册流程：
     * 1、前端通过云函数获取openId
     * 2、传到后端
     * @param studentVO
     * @return
     */
    @Transactional
    @Override
    public int regist_student(StudentVO studentVO) {
        /**
         * 封装Student信息
         */
        Student student =studentService.makeStudent(studentVO);

        /**
         * 封装完以后，不是插入Student，而是比对student信息，
         * 1、信息一致
         * 2、默认审核通过
         * 如果信息一致，将对应信息插入用户学生关系表，返回审核通过
         */
        Student student1=studentMapper.selectById(student.getStuId());

        //信息不一致就返回错误信息
        Assert.isTrue(student.equals(student1), ResponseEnum.STUDENT_TEACHER_INFORMATION_NOT_EQUAL);
        //信息一致：将信息插入关系表，创造token，保存信息到redis
        //1、将信息插入关系表
        UserTeacherStudent userTeacherStudent=new UserTeacherStudent();
        userTeacherStudent.setStuId(student.getStuId());
        /**
         * redis缓存中查询
         * 获取user通过openid
         * 缓存没有再从数据库中找
         */
        User user=getUserByOpenId(studentVO.getOpenId());

        userTeacherStudent.setUseId(user.getId());
        //暂时给予审核通过
        userTeacherStudent.setCheckState(true);
        //插入关系表
        int result=userTeacherStudentMapper.insert(userTeacherStudent);
        //2、创造token？？有问题
        String token= JWTUtils.sign(user.getId());
        //3、保存信息到redis
        UserDto userDto=new UserDto();
        userDto.setUser(user);
        userDto.setStudent(student);

        Academy academy=academyService.getAcademyByacaName(studentVO.getAcademy());
        Grade grade=gradeService.getGradeBygraName(studentVO.getGrade());

        userDto.setAcademy(academy);
        userDto.setGrade(grade);


        redisTemplate.opsForValue().set(RedisKey.TOKEN+token, JSON.toJSONString(userDto));

        return result;

    }

    @Override
    public String getOpenIdByStuUseId(Integer stuUseId) {
        return getUserById( userTeacherStudentMapper
                .selectById(stuUseId)
                .getUseId()).getOpenId();

    }

    private boolean  getStudent_bind(String openId){
        int i=0;

        while (i<redisTemplate_obj.opsForList().size(RedisKey.UPDATE_BIND)){

            ConcurrentHashMap<String,Object> map= (ConcurrentHashMap<String, Object>) redisTemplate_obj.opsForList().index(RedisKey.UPDATE_BIND,i);
            if (map.get("openId").equals(openId)){
                return true;
            }
            i++;

        }

        return  false;
    }
}
